type KeyValueObject = Record<string, any>;

export class ObjectUtil {
  /**
     * 一次性取多个值

     * @param {Object} target 目标对象
     * @param {Object} keys []
     */
  static getValueForKeys(target: KeyValueObject, keys: string[]) {
    if (typeof target !== 'object') {
      return null;
    }
    if (!Array.isArray(keys)) {
      return null;
    }

    return keys.map((key) => target[key] || null);
  }

  /**
     * 一次性设置多个值

     * @param {Object} target 目标对象
     * @param {Object} keys []
     * @param {Object} values []
     */
  static setValuesForKeys<T extends KeyValueObject, K extends string>(
    target: T,
    keys: K[],
    values: any[],
  ) {
    if (!values?.length) {
      return target;
    }
    if (typeof target !== 'object') {
      return target;
    }
    if (!Array.isArray(keys)) {
      return target;
    }
    keys.forEach((key, i) => (target[key] = values[i]));
    return target;
  }

  /**
     * 通过路径取值

     * @param {Object} target 目标对象
     * @param {Object} path 路径 'aa.bb' || 'aa/bb'
     */
  static getValueForPath(target: KeyValueObject, path: string) {
    if (typeof target !== 'object') {
      return null;
    }
    let _path = path.replaceAll('/', '.');
    const paths = _path.split('.');
    return paths.reduce((o, p) => {
      let _o = o || {};
      return _o[p];
    }, target);
  }

  /** 通过路径设置值
   * @param {Object} target 目标对象
   * @param {Object} path 路径 'aa.bb' || 'aa/bb'
   * @param {Object} value 值
   */
  static setValueForPath<T extends KeyValueObject>(target: T, path: string, value: any) {
    if (typeof target !== 'object') {
      return target;
    }
    let _path = path.replaceAll('/', '.');
    const paths = _path.split('.');
    let tempTarget = target;
    paths.forEach((v: keyof T, i) => {
      if (i === paths.length - 1) {
        tempTarget[v] = value;
      } else {
        tempTarget[v] = tempTarget[v] || ({} as any);
        tempTarget = tempTarget[v] || ({} as any);
      }
    });
    return target;
  }

  /**
     * 通过key取值

     * @param {Object} target
     * @param {Object} anyKey aa.bb || aa/bb || aa,bb  其中aa,bb 返回的格式也是aa,bb
     */
  static getValue(target: KeyValueObject, anyKey: string) {
    if (!target) {
      return null;
    }
    if (!anyKey) {
      console.warn('无有效key', anyKey, target);
      return null;
    }

    if (anyKey.includes(',')) {
      const values = this.getValueForKeys(target, anyKey.split(','));
      return values?.every((v) => v === null) ? null : values?.join(',');
    }
    if (anyKey.includes('.') || anyKey.includes('/')) {
      return this.getValueForPath(target, anyKey);
    }
    if (Array.isArray(anyKey)) {
      return this.getValueForPath(target, anyKey.join('.'));
    }
    return target[anyKey];
  }

  /**
     * 通过key赋值

     * @param {Object} target 目标对象
     * @param {Object} anyKey anyKey aa.bb || aa/bb || aa,bb  其中aa,bb value格式也要是aa,bb
     * @param {Object} value
     */
  static setValue<T extends KeyValueObject, K extends string>(target: T, anyKey: K, value: any): T {
    if (!target) {
      return target;
    }
    if (anyKey.includes(',')) {
      return this.setValuesForKeys(target, anyKey.split(','), value?.split(','));
    }
    if (anyKey.includes('.') || anyKey.includes('/')) {
      return this.setValueForPath(target, anyKey, value);
    }
    if (Array.isArray(anyKey)) {
      return this.setValueForPath(target, anyKey.join('.'), value);
    }
    target[anyKey] = value;
    return target;
  }

  /**
     * 判断是否存在多个key 同时存在为true 否则为false

     * @param {Object} target 目标对象
     * @param {Object} keys key 数组
     */
  static hasKeys(target: KeyValueObject, keys: string) {
    if (!keys.length) {
      return false;
    }
    return Object.keys(target).reduce((o, p) => o && keys.includes(p), true);
  }

  /**
     * 判断对象指定path是否存在

     * @param {Object} target 目标对象
     * @param {Object} path 路径 aa.bb || aa/bb
     */
  static hasPath(target: KeyValueObject, path: string) {
    let _path = path.replaceAll('/', '.');
    let _target = target;
    const paths = _path.split('.');
    for (let i = 0; i < paths.length; i++) {
      if (!Object.keys(_target).includes(paths[i])) {
        return false;
      } else {
        _target = _target[paths[i]];
      }
    }
    return true;
  }

  /**
     * 判断对象是否存在指定key

     * @param {Object} target 目标对象
     * @param {Object} anyKey anyKey aa.bb || aa/bb || aa,bb  其中aa,bb需同时满足才为true
     */
  static hasKey(target: KeyValueObject, anyKey: string) {
    if (!target) {
      return false;
    }
    if (anyKey.includes(',')) {
      return this.hasKeys(target, anyKey);
    }
    if (anyKey.includes('.') || anyKey.includes('/')) {
      return this.hasPath(target, anyKey);
    }

    return Object.keys(target).includes(anyKey);
  }

  static removeUndefinedFields = (target: KeyValueObject) => {
    if (typeof target !== 'object' || target === null) {
      return target;
    }

    if (Array.isArray(target)) {
      return target.filter((item) => typeof item !== 'undefined');
    }

    for (const key in target) {
      if (typeof target[key] === 'undefined') {
        delete target[key];
      } else if (typeof target[key] === 'object') {
        target[key] = this.removeUndefinedFields(target[key]);
      }
    }

    return target;
  };

  static removeEmptyObjects(obj: KeyValueObject) {
    for (const key in obj) {
      if (obj[key] && typeof obj[key] === 'object') {
        // 递归调用，移除嵌套对象中的空对象
        obj[key] = this.removeEmptyObjects(obj[key]);

        // 如果嵌套对象变为空对象，删除该键值对
        if (Object.keys(obj[key]).length === 0 && typeof obj[key] !== 'function') {
          delete obj[key];
        }
      } else if (obj[key] === null || obj[key] === undefined || obj[key] === '') {
        // 移除值为 null、undefined 或空字符串的键值对
        delete obj[key];
      }
    }
    return obj;
  }

  static deepClone(obj: KeyValueObject) {
    if (obj === null || typeof obj !== 'object') {
      return obj;
    }

    let clonedObj: any = Array.isArray(obj) ? [] : {};

    for (let key in obj) {
      if (obj.hasOwnProperty(key)) {
        clonedObj[key] = this.deepClone(obj[key]);
      }
    }
    return clonedObj;
  }
}
